package org.third.security.crypto;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import java.math.BigInteger;
import java.security.MessageDigest;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <pre>
 * MD5、SHA、HMAC这三种加密算法，可谓是非可逆加密，就是不可解密的加密方法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。
 * 
 * Base64:
 * 
 * 按照RFC2045的定义，Base64被定义为：Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。（The
 * Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences
 * of octets in a form that need not be humanly readable.）
 * 
 * 常见于邮件、http加密，截取http信息，你就会发现登录操作的用户名、密码字段通过BASE64加密的。
 * BASE加密后产生的字节位数是8的倍数，如果不够位数以=符号填充
 * 
 * 
 * MD5 MD5 -- message-digest algorithm 5
 * （信息-摘要算法）缩写，广泛用于加密和解密技术，常用于文件校验。校验？不管文件多大，经过MD5后都能生成唯一的MD5值。好比现在的ISO校验，
 * 都是MD5校验。怎么用？当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。
 * 就是用来验证文件是否一致的
 * 
 * 通常我们不直接使用上述MD5加密。通常将MD5产生的字节数组交给BASE64再加密一把，得到相应的字符串。
 * 
 * SHA SHA(Secure Hash Algorithm，安全散列算法）
 * 数字签名等密码学应用中重要的工具，被广泛地应用于电子商务等信息安全领域。虽然，SHA与MD5通过碰撞法都被破解了，
 * 但是SHA仍然是公认的安全加密算法，较之MD5更为安全
 * 
 * HMAC( Hash Message Authentication Code)
 * 散列消息鉴别码，基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是，用公开函数和密钥产生一个固定长度的值作为认证标识，
 * 用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块，即MAC，并将其加入到消息中，然后传输。 接收方利用与发送方共享的密钥进行鉴别认证等。
 * 
 * <pre>
 * 
 * @author gongyo
 *
 */
public class UnidirectionalEncryption {
    static final Logger logger = LoggerFactory.getLogger(UnidirectionalEncryption.class);
    public static final String plainText = "(plain Text )";

    public static final String password = "pass-word";

    public static byte[] encryptMD5(byte[] data) throws Exception {

        MessageDigest sha = MessageDigest.getInstance(CryptoAlgorithm.MD5.getName());
        sha.update(data);

        return sha.digest();

    }

    public static byte[] encryptSHA(byte[] data) throws Exception {

        MessageDigest sha = MessageDigest.getInstance(CryptoAlgorithm.SHA.getName());
        sha.update(data);

        return sha.digest();

    }

    javax.crypto.SecretKey hmacSecretKey = null;

    {

    }

    public static byte[] encryptHMAC(byte[] data) throws Exception {
        // 初始化HMAC密钥
        javax.crypto.KeyGenerator keyGenerator = KeyGenerator.getInstance(CryptoAlgorithm.HmacMD5.getName());
        javax.crypto.SecretKey secretKey1 = keyGenerator.generateKey();

        // 加密
        SecretKey secretKey2 = new SecretKeySpec(secretKey1.getEncoded(), CryptoAlgorithm.HmacMD5.getName());
        javax.crypto.Mac mac = Mac.getInstance(secretKey2.getAlgorithm());
        mac.init(secretKey2);

        return mac.doFinal(data);

    }

    @Test
    public void test() throws Exception {
        String inputStr = "Protected Data";
        System.err.println("原文:" + inputStr);

        byte[] inputData = inputStr.getBytes();
        String code = CryptoAlgorithm.base64Encode(inputData);
        System.err.println("BASE64加密后:" + code);

        byte[] output = CryptoAlgorithm.base64Decode(code);
        String outputStr = new String(output);
        System.err.println("BASE64解密后:" + outputStr);

        // 验证BASE64加密解密一致性
        assertEquals(inputStr, outputStr);

        // 验证MD5对于同一内容加密是否一致
        assertArrayEquals(encryptMD5(inputData), encryptMD5(inputData));

        // 验证SHA对于同一内容加密是否一致
        assertArrayEquals(encryptSHA(inputData), encryptSHA(inputData));

        // 验证HMAC对于同一内容，同一密钥加密是否一致
        assertArrayEquals(encryptHMAC(inputData), encryptHMAC(inputData));

        BigInteger md5 = new BigInteger(encryptMD5(inputData));
        System.err.println("MD5:\n" + md5.toString(16));

        BigInteger sha = new BigInteger(encryptSHA(inputData));
        System.err.println("SHA:\n" + sha.toString(32));

        BigInteger mac = new BigInteger(encryptHMAC(inputData));
        System.err.println("HMAC:\n" + mac.toString(16));
    }
}
